package main

import (
	"fmt"
	"sync"
	"time"
)

// sync.Cond
// https://blog.csdn.net/ma_jiang/article/details/134022579

// Cond用于多个（读写），chan只适合单个（读写）

// 关键的一点是 cond.Wait() 会先Unlock()，所以在Wait()之前需要手动Lock()，否则报错。

/*
https://blog.csdn.net/rubys007/article/details/135494113

很多并发编程的问题都可以通过 channel 来解决。

同样的，在上面提到的 sync.Cond 的使用场景，使用 channel 也是可以实现的，
我们只要 close(ch) 来关闭 channel 就可以实现通知多个等待的协程了。

那么为什么还要用 sync.Cond 呢？

主要原因是，sync.Cond 可以重复地进行 Wait() 和 Signal()、Broadcast() 操作，
但是，如果想通过关闭 chan 来实现这个功能的话，那就只能通知一次了。
因为 channel 只能关闭一次，关闭一个已经关闭的 channel 会导致程序 panic。

使用 channel 的另外一种方式是，记录 reader 的数量，然后通过往 channel 中发送多次数据来实现通知多个 reader。

但是这样一来代码就会复杂很多，从另一个角度说，出错的概率大了很多。

// For many simple use cases, users will be better off using channels than a
// Cond (Broadcast corresponds to closing a channel, and Signal corresponds to
// sending on a channel).

sync.Cond 利用 sync.Mutex 来按序入队
*/
func main() {

	go func() {
		for {
			<-time.After(time.Second)
			fmt.Println(time.Now().Second(), ":::")
		}
	}()

	mutex := sync.Mutex{}
	cond := sync.NewCond(&mutex)
	x := false

	for i := 0; i < 10; i++ {

		go func(i int) {
			mutex.Lock()         //
			defer mutex.Unlock() //为啥呢？感觉没必要。除非Signal/Broadcast会加锁

			//想象一下，如果不先加锁，那这里可能会出现异常情况，即x的值不是真实值
			//另外，这里如果不循环验证，那唤醒后可能又发生了变化
			for !x {
				fmt.Println("to Wait - ", i)
				cond.Wait() //在调用Wait之前，必须先Lock
			}

			fmt.Println("after Wait 1 - ", i)
			<-time.After(time.Second * 5)
			fmt.Println("after Wait 2 - ", i)

		}(i)
	}

	go func() {

		cond.L.Lock()
		<-time.After(time.Second * 2)

		//逻辑完成后，设置状态
		x = true
		cond.L.Unlock() //注意，先解锁，再发信号或广播

		fmt.Println("to Signal")
		//cond.Signal() //解放一个
		cond.Broadcast() //解放所有

		fmt.Println("after Signal")
	}()

	<-time.After(time.Second * 30)
	fmt.Println("-----------")

}
